0// Deshabilitamos seleccion de texto

function disableselect(e) {return false}
function reEnable() {return true}
//if IE4+
document.onselectstart=new Function ("return false")
//if NS6
if (window.sidebar) {
   document.onmousedown=disableselect
   document.onclick=reEnable
};

/*
 Script: dxd.base.class-1.21.js
 autor: Damian Suarez
 email: damian.suarez@xifox.net
 Contiene las clases <DxD>, <DxD.Film> y <DxD.Swap>
 Class: DxD
 Clase Base para implementar comportamientos Drag And Drops.
 Note:
 DxD.Base requiere un doctype XHTML.
 License:
 Licencia MIT-style.
 
 Argumentos:
 drags - Conjunto de Elementos Draggeables
 drops - Conjunto de Elementos Droppeables
 options - Opciones de configuracion.
 options:
 var: adjDrags
 Vector de configuracion en formato JSon para los elementos drags. {x: , y: }.
 *default* = [].
 var: adjDrops
 Vector de configuracion en formato JSon para los elementos drops. {x: , y: }.
 *default* = [].
 var: autoLevel
 Habilita (o deshabilita) el comportamiento de elevacion de capas automatico de los drags.
 *default* = true.
 Ver Tambien <zIndexBottom>, <zIndexTop>.
 var: zIndexTop
 Valor z-index al cual se 'elevan' los elementos drags sobre un evento mouseenter y mousedown.
 *default* = 1000.
 Ver tambien <zIndexBottom>, <autoLevel>.
 var: zIndexBottom
 Valor z-index del elemento drag mas bajo.
 
 *default* = 0.
 Los otros elementos drags que configuran con un z-index ascendente en una unidad.
 Ver tambian <zIndexTop>, <autoLevel>.
 var: dragDelete
 Elimina u oculta el drag cuando este 'droppea' exitosamente (evento onTrueDrop).
 *default* = false.
 Puede tomar los siguientes valores:
 false - no hace nada.
 true - elimina el nodo/elemento drag
 string 'none' - Aplica la propiedad de css display:none al elemento drag.
 var: multidrop
 *default* = false. Permite realizar multiples Drops. Su valor puede ser:
 booleano (false) - Solo permite un solo drop.
 entero - define la cantidad de eventos drops que se pueden hacer en cada elemento drop.
 array - donde se especifique la cantidad de eventos drops por cada uno de los drops. (alpha)-
 string 'infinity' - Puede realizar infinitos drops.
 var: sendToBack
 *default* = false.
 Propiedad que define el modo de _volver_ de un elemento drag cuando no se producen ciertas condiciones al realizar un drop. Puede tomar los siguientes valores:
 false - No realiza ninguna accion.
 'ini' - Vuelve a la posicion inicialmente configurada en <adjDrags> tanto en un <emptydrop> como en un <emptyTrueDrop>.
 'back' - Vuelve a la posicón inicial anterior al intentar hacer un <trueDrop>.
 'toDrop' - Vuelve a la posicion del drop de donde salió. Se utiliza para modo Swap.
 var: cssClass
 *defaul* = false. Definimos la cabecera del nombre de la clase CSS que se aplica tanto a los drags como a los drops. Automaticamente se defefinen 6 clases CSS
 iniDrag - clase CSS aplicada inicialmente a los elementos Drags.
 enterDrag - clase CSS que se aplica a los drags elementos cuando se produce un evento 'mouseenter'
 dragDrag - clase CSS que se aplica a los drags elementos cuando se los draguea.
 
 iniDrop - clase CSS aplicada inicialmente a los elementos Drops.
 overDrop - clase CSS que se aplica al drop al producirse evento 'drop'.
 trueDrop - clase CSS que se aplica al drop al producirse un drop verdadero.
 
 var: optionsFxDrag
 Parametros de configuracion del efecto que se produce en los drags
 *default* : {
 wait: false,
 duration: 500,
 transition: Fx.Transitions.Back.easeOut
 }
 var: optionsFxDrop
 Parametros de configuracion del efecto que se produce en los drops.
 *default*: Idem a <optionsFxDrag>.
 Eventos:
 La clase incorpora una cantidad considerable de eventos.
 
 Eventos heredados de Drag-Move:
 
 onEmptyDrop:
 onDrop:
 
 onDragComplete:
 onDragOverDrag: Se dispara cuando hemos droppeado un drag sobre otro drag.
 onEmptyOverDrag:
 onBeforeDrag:
 
 onTrueDrop: Se dispara cuando se produce un drop verdadero; esto es, cuando se dropea validando la logica definida en <fncTrueDrop>.
 onEmptyTrueDrop: Se dispara cuando, a pesar de producrise un evento drop, este es incorrecto en funcion de <fncTrueDrop>.
 onMultiDrop: Se dispara cuando se ha llegado al numero de <multiDrop> definido.
 nota: Es importante notar que los eventos se disparan tanto en el objeto que se instancia de DxD.Base y, dependiendo de la naturaleza del mismo, tambien se disparan en los elemento/objetos Drags o Drops.
 */
var DxD = new Class({
	Implements: [Events, Options],
	// Opciones de nuestra clase
	options: {
		adjDrags: [],
		adjDrops: [],
		autoLevel: true,
		zIndexTop: 1000,
		zIndexBottom: 100,
		
		dragDelete: false,
		multiDrop: false,
		sendToBack: false,
		
		cssClass: false,
		textInDrop: true,

		optionsDrag: {
			snap: 6,
			unit: 'px',
			grid: false,
			style: true,
			limit: false,
			handle: false,
			invert: false,
			preventDefault: false,
			modifiers: {x: 'left', y: 'top'}
		},

		optionsFxDrag: {
			wait: false,
			duration: 500,
			transition: Fx.Transitions.Back.easeOut
		},
		optionsFxDrop: {
			wait: false,
			duration: 800,
			transition: Fx.Transitions.Back.easeOut
		},
		/*
		onTrueDrop: Class.empty,
		onEmptyTrueDrop: Class.empty,
		onMultiDrop: Class.empty,
		onBeforeDrag: Class.empty,
		onDragComplete: Class.empty,
		onDropsComplete: Class.empty,
		onReady: Class.empty,
		*/
		opacity: {
			'drag': 1,
			'drop': 1,
			'dragDrag': 0.8,
			'overDrag': 0.3,
			'overDrop': 0.8,
			'trueDrop': 1
		}
		//enableSnds: [1, 1, 1, 1, 1, 1]			// enterDrag, leaveDrag, dragDrag, overDrop, emptyDrop, trueDrop
	},

	// Constructor
	initialize: function(drags, drops, options){
		// Seteamos elementos de la clase.
		this.setOptions(options);

		// Vector de los objetos Drags
		this.htmlDrags = new Array ();
		this.elDrags = new Array ();

		// Vector de los objetos drops.
		this.htmlDrops = new Array ();
		this.elDrops = new Array ();

		// dragAct. Variable que contiene el elemento Drag activo.
		this.dragAct = false;

		// Orden de los drags
		this.ordDrag = [];
//		this.arrOvered = [];

		// Drag in Drop. Esta variable booleana define si el drag estuvo o no estuvo sobre un drop.
		this.dragInDrop = false;

		// Generamos Drags y Drops gracias a <Drag.Move>
		this.setIniBase(drags, drops);
		
		// Agregamos evento d complete
		//this.addEvent ('complete', this.dragComplete);

	},


	/*
	method - setIniBase
	Se generan comportamiento Drag and Drop Base
	parametros:
	drags - Conjuntno de elementos Drags.
	drops - Conjunto de elementos Drops.
	ver Tambien:
	<addDrag>
	*/	
	setIniBase: function(drags, drops){

		// Seteos Iniciales de Drag
		this.document = $(drags[0]).getDocument();
		this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';
		
		// Seteos de Drag.Move
		this.container = $(this.options.container);

		// Redefimos multiDrop en caso de que sea false. Esto es porque si es false, es lo mismo que se permita UN (1) solo drop.
//		if (!this.options.multiDrop) this.options.multiDrop = 1;

		// Recorremos Drops
		drops.each(function($htmlNode, iDrop){
			// Llenamos el array de todos los nodos HTML drops
			this.htmlDrops.include ($($htmlNode));

			// Llenamos el array de los objetos Drops
			this.elDrops.include ({
				ind: iDrop,
				indDrag: new Boolean (false),
				qttDrops: 0,
				pos: this.getPos($htmlNode),
				text: $htmlNode.get('text'),
				fx: new Fx.Morph($htmlNode, this.options.optionsFxDrop)
			});
			
			// definimos opacidad
			//$htmlNode.setOpacity(this.options.opacity.drop);

			// Clases Css iniciales ?
			if (this.options.cssClass) $htmlNode.addClass(this.options.cssClass + '-iniDrop');

			// Ahora definirmos una propiedad de cada drop denominada 'numDrops'.
			// Esta propiedad define la candidad de drops que se pueden realizar en cada uno de ellos.
			/*if ($type(this.options.multiDrop) == 'number') $htmlNode.numDrops = this.options.multiDrop;
			else 
				if ($type(this.options.multiDrop) == 'array') 
					$htmlNode.numDrops = this.options.multiDrop[iDrop];

			$htmlNode.getQttDrops = function(){
					return ($htmlNode.qttDrops - 1);
			}
			*/
		}, this);
		

		/*************
		 *** Drags ***
		 *************/
		drags.each(function($htmlNode, $iObj){

			// Llenamos el array de todos los nodos HTML drags
			this.htmlDrags.include ($($htmlNode));

			// Vamos llenando array con objetos Drags con todas sus propiedades
			this.elDrags.include ({
				ind: $iObj,
				indDrop: false,
				zIndex: $iObj + this.options.zIndexBottom,
				fx: new Fx.Morph($htmlNode, this.options.optionsFxDrag)
			});

			// Propiedades CSS Iniciales
			//$htmlNode.setOpacity(this.options.opacity.drag);							// Opacidad
			$htmlNode.setStyle('z-index', $iObj + this.options.zIndexBottom);			// Nivel con z-index

			// Vector que guarda el orden en Z de los drags
			this.ordDrag[$iObj] = $iObj;

			// Clases Css iniciales ?
			if (this.options.cssClass) $htmlNode.addClass(this.options.cssClass + '-iniDrag');

			// Eventos de elementos Drags
			$htmlNode.addEvents ({
				'mouseenter': function(el){
					if (this.options.autoLevel) this.raseUpDrag(this.elDrags[$iObj]);
					if (this.options.cssClass) this.htmlDrags[$iObj].addClass (this.options.cssClass + '-enterDrag');
//					if (this.options.cssClass) this
				}.bind(this),
				'mouseleave': function(){
					if (this.options.autoLevel) this.levelDrags(this.elDrags[$iObj])
					if (this.options.cssClass) this.htmlDrags[$iObj].removeClass (this.options.cssClass + '-enterDrag')
				}.bind(this)
			});			

			// Creamos propiedades de Drag (antes se usaba la clase Drag.Move)
			this.createDrag (this.elDrags[$iObj]);


			// Hacemos a todos los drags draggables.
			/*
			this.elDrags[$iObj].Move = new DragFox.MovePlus($($htmlNode), {
				droppables: this.drops,
				container: this.options.container,
				onBeforeStart: function ($htmlNode, $event) {
					this.start(this.elDrags[$iObj], $event);
				}.bind(this),
				onComplete: function($htmlNode) {

					// Recuperamos Array de drags solapados.
					//var arrTmp = this.elDrags[$iObj].Move.out ? false : this.elDrags.filter(this.elDrags[$iObj].Move.checkAgainst, this.elDrags[$iObj].Move);
					//this.arrOvered = arrTmp ? arrTmp.copy() : false;
					//this.arrOvered = arrTmp ? $A(arrTmp) : false;
					
					
					// Llamamos a metodo privado dragComplete
					this.dragComplete($iObj);

                }.bind(this),
                
                // onDrop. Evento agregados en 1.2.1. Antes el eveto se producía en un drop. 
                onDrop: function($$htmlNode, $droppable){
					
			        if ($droppable) {
			        	// Tenemos un drag en un drop, entonces ...
			        	this.dropComplete($$htmlNode, $droppable);
			        	this.dragInDrop = true;
			        }
			        else {
			        	// Disparamos evento sobre la clase principal
						this.fireEvent('emptyDrop', $htmlNode);

						// Comportamientos de los Drags en 'emptydrag'
						if (this.options.sendToBack) {

							switch (this.options.sendToBack) {

								case 'ini':
									// Reasignamos coordenadas solo por las dudas.
									this.elDrags[$iObj].pos.x = this.options.adjDrags[$iObj].x;
									this.elDrags[$iObj].pos.y = this.options.adjDrags[$iObj].y;
									this.sendToBack($iObj);
								break;

								// Redefinimos nuevas coordenadas
								case 'back':
									//drag.Pos.x = drag.getStyle ('left').toInt();
									//drag.Pos.y = drag.getStyle ('top').toInt();
								break;
								
								case 'toDrop':
									if ($htmlNode.indDrop != -1) {
										// Enviamos pal'drop. Ya previamente asignado en dropComplete.
										this.sendToBack($htmlNode);
		                            };
								break;
							};
						};
			        }
				}.bind(this),

				onEnter: function($htmlNode, $droppable){
					if (($droppable.qttDrops <= $droppable.numDrops) || this.options.multiDrop == 'infinity') {
						// drag.fxDrag.start ({opacity: this.options.opacity.overDrag});
						// drop.fxDrop.start ({opacity: this.options.opacity.overDrop});
						$htmlNode.setOpacity(this.options.opacity.overDrag);
						$droppable.setOpacity(this.options.opacity.overDrop);

						if (this.options.textInDrop) {
							$droppable.set('text', $htmlNode.get('text')); // Seteamos el texto del drop con el texto que tiene el drag.
						};
					};
				}.bind(this),

				onLeave: function($htmlNode, $droppable) {
					if (($droppable.qttDrops <= $droppable.numDrops) || this.options.multiDrop == 'infinity') {
						// $htmlNode.fxDrag.start ({opacity: this.options.opacity.dragDrag});
						// drop.fxDrop.start ({opacity: this.options.opacity.drop});
						$htmlNode.setOpacity(this.options.opacity.dragDrag);
						$droppable.setOpacity(this.options.opacity.$droppable);

						if (this.options.textInDrop) $droppable.set('text', $droppable.text);
                    };
				}.bind(this)

            });  // -> End this.elDrags[$iObj].Move
*/
		}, this); // end Drags
		// Ajustamos coordenadas de Drags y Drops
		//if ((this.options.adjDrags).length) this.adjustDrags();
		this.adjustDrags();
		if ((this.options.adjDrops).length) this.adjustDrops();
	},
	
	/*
	 * Metodo que implementa todas las condiciones iniciales para hacer Drags a los nodos. Reemplaza al construcutor de la clase Drag.
	 */
	createDrag: function ($objDrag, $options) {
		$htmlDragNode = this.htmlDrags[$objDrag.ind];

		// Parametros de configuracion. Es uno de los puntos mas importantes a arreglar
		var params = Array.link([$htmlDragNode, this.options.optionsDrag], {'options': Object.type, 'element': $defined});

		//this.element = $(params.element); 						-> es el nodo HTML del drag. Lo tenemos en $objDrag.htmlNode
		//this.document = $objDrag.htmlNode.getDocument();			-> es el documento. Lo metenmos fuera de los drags.each

		this.setOptions(params.options || {});
		var htype = $type(this.options.optionsDrag.handle);		//	-> handle es una propiedad de cada drag
		
		// handles son las areas donde podemos 'agarrar' al drag.
		//this.handles = (htype == 'array' || htype == 'collection') ? $$(this.options.handle) : $(this.options.handle) || this.element;
		$objDrag.handles = (htype == 'array' || htype == 'collection') ? $$(this.options.optionsDrag.handle) : $(this.options.optionsDrag.handle) || $htmlDragNode;
		
		// mouse Guarda las posiciones del drag.
		//this.mouse = {'now': {}, 'pos': {}};
		$objDrag.mouse = {'now': {}, 'pos': {}, 'start': {}};

		//this.value = {'start': {}, 'now': {}};
		$objDrag.value = {'now': {}};
		
		$objDrag.pos = {left: null, top: null, right: null, bottom: null};
		
		

		// Define el tipo de selección en funcion del browser
		//this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';					-> Esta definido dentro de la clase DxD.

		$objDrag.bound = {
			start: function($event){this.start($objDrag, $event)}.bind(this),
			check: function($event){this.check($objDrag, $event)}.bind(this),
			drag: function($event){this.drag($objDrag, $event)}.bind(this),
			stop: function($event){this.stop($objDrag, $event)}.bind(this),
			cancel: function($event){this.cancel($objDrag, $event)}.bind(this),
			eventStop: $lambda(false)
		};
		
		this.attach($objDrag);

		// Lo que viene es parte del contructor Drag.Move
		if (this.container && $type(this.container) != 'element') this.container = $(this.container.getDocument().body);
		
		var current = $htmlDragNode.getStyle('position');
		var position = (current != 'static') ? current : 'absolute';
		if ($htmlDragNode.getStyle('left') == 'auto' || $htmlDragNode.getStyle('top') == 'auto') $htmlDragNode.position($htmlDragNode.getPosition($htmlDragNode.offsetParent));
		
		$htmlDragNode.setStyle('position', position);

		// Agregamos funcion al evento start
		this.addEvent('start', function(){
			this.checkDroppables($objDrag);
		}, true);
	},
	
	// Antes de que comience el drag
	start: function($objDrag, $event){
		$htmlNodeDrag = this.htmlDrags[$objDrag.ind];
		
		$objDrag.pos = this.getPos ($htmlDragNode);

		// Nivelamos en el start si es que no está en forma automática
		if (!this.options.autoLevel) this.raseUpDrag($objDrag);

		// Parte de Drag.Move
		if (this.container){
			var el = $htmlNodeDrag, cont = this.container, ccoo = this.container.getCoordinates(el.offsetParent), cps = {}, ems = {};

			['top', 'right', 'bottom', 'left'].each(function(pad){
				cps[pad] = cont.getStyle('padding-' + pad).toInt();
				ems[pad] = el.getStyle('margin-' + pad).toInt();
			}, $objDrag);

			var width = el.offsetWidth + ems.left + ems.right, height = el.offsetHeight + ems.top + ems.bottom;
			var x = [ccoo.left + cps.left, ccoo.right - cps.right - width];
			var y = [ccoo.top + cps.top, ccoo.bottom - cps.bottom - height];

			this.options.optionsDrag.limit = {x: x, y: y};
		};
		
		// Disparamos evento en clase
		this.fireEvent('beforeStart', [$objDrag]);

		
		if (this.options.optionsDrag.preventDefault) $event.preventDefault();
		$objDrag.mouse.start = $event.page;

		var $limit = this.options.optionsDrag.limit;
		$objDrag.limit = {'x': [], 'y': []};

		for (var z in this.options.optionsDrag.modifiers){
			if (!this.options.optionsDrag.modifiers[z]) continue;
			if (this.options.optionsDrag.style) $objDrag.value.now[z] = $htmlNodeDrag.getStyle(this.options.optionsDrag.modifiers[z]).toInt();
			else $objDrag.value.now[z] = $htmlNodeDrag[this.options.optionsDrag.modifiers[z]];
			if (this.options.optionsDrag.invert) $objDrag.value.now[z] *= -1;
			$objDrag.mouse.pos[z] = $event.page[z] - $objDrag.value.now[z];
			if ($limit && $limit[z]){
				for (var i = 2; i--; i){
					if ($chk($limit[z][i])) $objDrag.limit[z][i] = $lambda($limit[z][i])();
				}
			}
		};

		if ($type(this.options.optionsDrag.grid) == 'number') this.options.optionsDrag.grid = {'x': this.options.optionsDrag.grid, 'y': this.options.optionsDrag.grid};

		this.document.addEvents({mousemove: $objDrag.bound.check, mouseup: $objDrag.bound.cancel});
		this.document.addEvent($objDrag.selection, $objDrag.bound.eventStop);


		// Disparamos envento en clase y en drag
		//drag.fireEvent('beforeDrag', drag);
		//this.fireEvent('beforeDrag', drag);
/*

		this.dragInDrop = false;

		// Definimos Drag Activo
		this.dragAct = $objDrag.htmlNode;

		// Opacidad
		//	drag.fxDrag.start ({opacity: this.options.opacity.dragDrag});
		$objDrag.htmlNode.setOpacity(this.options.opacity.dragDrag);

		// Reasignacion de posiciones del Drag
		switch (this.options.sendToBack) {
			case 'back':
				drag.Pos.x = drag.getStyle('left').toInt();
				drag.Pos.y = drag.getStyle('top').toInt();
			break;
		};

		// autoLevel ?. Si es asi ... levantanamos el drag.
		if (this.options.autoLevel) this.raseUpDrag($objDrag);
		*/

	},
	
	check: function ($objDrag, $event) {
		$htmlDragNode = this.htmlDrags[$objDrag.ind];

		if (this.options.optionsDrag.preventDefault) $event.preventDefault();
		var distance = Math.round(Math.sqrt(Math.pow($event.page.x - $objDrag.mouse.start.x, 2) + Math.pow($event.page.y - $objDrag.mouse.start.y, 2)));
		
		if (distance > this.options.optionsDrag.snap){
			this.cancel($objDrag, $event);
			
			this.document.addEvents({
				mousemove: $objDrag.bound.drag,
				mouseup: $objDrag.bound.stop
			});
			this.fireEvent('start', $htmlDragNode).fireEvent('snap', $htmlDragNode);
		}
	},

	drag: function ($objDrag, $event) {
		// CSS
		if (this.options.cssClass) $htmlNodeDrag.addClass(this.options.cssClass + '-dragDrag');

		$htmlDragNode = this.htmlDrags[$objDrag.ind];

		$objDrag.pos = this.getPos ($htmlDragNode);

		if (this.options.optionsDrag.preventDefault) $event.preventDefault();
		$objDrag.mouse.now = $event.page;
		for (var z in this.options.optionsDrag.modifiers){
			if (!this.options.optionsDrag.modifiers[z]) continue;
			$objDrag.value.now[z] = $objDrag.mouse.now[z] - $objDrag.mouse.pos[z];
			
			if (this.options.optionsDrag.invert) $objDrag.value.now[z] *= -1;
			if (this.options.optionsDrag.limit && $objDrag.limit[z]){
				if ($chk($objDrag.limit[z][1]) && ($objDrag.value.now[z] > $objDrag.limit[z][1])){
					$objDrag.value.now[z] = $objDrag.limit[z][1];
				} else if ($chk($objDrag.limit[z][0]) && ($objDrag.value.now[z] < $objDrag.limit[z][0])){
					$objDrag.value.now[z] = $objDrag.limit[z][0];
				}
			}
			if (this.options.optionsDrag.grid[z]) $objDrag.value.now[z] -= ($objDrag.value.now[z] % $objDrag.options.optionsDrag.grid[z]);
			if (this.options.optionsDrag.style) $htmlDragNode.setStyle(this.options.optionsDrag.modifiers[z], $objDrag.value.now[z] + this.options.optionsDrag.unit);
			else $htmlDragNode[this.options.optionsDrag.modifiers[z]] = $htmlDragNode.value.now[z];
		};
		this.fireEvent('drag', $objDrag);
		
		// Drag.Move
		if (this.htmlDrops.length) this.checkDroppables($objDrag);
	},

	stop: function ($objDrag, $event) {

		if (this.options.cssClass) this.htmlDrags[$objDrag.ind].removeClass(this.options.cssClass + '-dragDrag');
		
		if (!this.options.autoLevel) this.levelDrags($objDrag);
		$htmlDragNode = this.htmlDrags[$objDrag.ind];
		
		/*
		this.checkDroppables();
		this.fireEvent('drop', [this.element, this.overed]);
		this.overed = null;
		*/
		
		this.checkDroppables($objDrag);
		
		if (this.overed) {
			this.fireEvent('drop', [$objDrag, this.overed]);
			$htmlDragNode.addClass (this.options.cssClass + '-dropDrag');
			this.htmlDrops[this.overed.ind].addClass (this.options.cssClass + '-dropDrop');
		} 
		else {
			$htmlDragNode.removeClass (this.options.cssClass + '-dropDrag');
		}
		this.overed = null;

		this.document.removeEvent(this.selection, $objDrag.bound.eventStop);
		this.document.removeEvent('mousemove', $objDrag.bound.drag);
		this.document.removeEvent('mouseup', $objDrag.bound.stop);
		if ($event) this.fireEvent('complete', $objDrag);

		// CSS
		//if (this.options.cssClass) $htmlNodeDrag.removeClass(this.options.cssClass + '-dragDrag');
	},

	cancel: function ($objDrag, $event) {
		this.document.removeEvent('mousemove', $objDrag.bound.check);
		this.document.removeEvent('mouseup', $objDrag.bound.cancel);

		if ($event){
			this.document.removeEvent(this.selection, $objDrag.bound.eventStop);
			this.fireEvent('cancel', $objDrag.ind);
		};
		
		// CSS
		//if (this.options.cssClass) $htmlNodeDrag.removeClass(this.options.cssClass + '-dragDrag');
	},
	
	checkAgainst: function($objD){
		var now = this.pos;
//		console.log (now);
//		now.r = now.x + this.dims.width;
//		now.b = now.y + this.dims.height;

//		console.log ((!(now.x > $objD.pos.right || now.r < $objD.pos.left || now.y > $objD.pos.bottom || now.b < $objD.pos.top)));

		return (!(now.left > $objD.pos.right || now.right < $objD.pos.left || now.top > $objD.pos.bottom || now.bottom < $objD.pos.top));
		//return (now.x > $objD.coords.left && now.x < $objD.coords.right && now.y < $objD.coords.bottom && now.y > $objD.coords.top);
	},

	checkDroppables: function($objDrag){
		var overed = this.elDrops.filter(this.checkAgainst, $objDrag).getLast();
		//console.log (overed);

		if (this.overed != overed) {

			if (this.overed) {
				this.fireEvent('leave', [$objDrag, this.overed]);
				this.htmlDrops[this.overed.ind].removeClass(this.options.cssClass + '-overDrop');
				
				this.htmlDrags[$objDrag.ind].removeClass(this.options.cssClass + '-overDrag');
				this.htmlDrags[$objDrag.ind].removeClass(this.options.cssClass + '-dropDrag');
				//this.htmlDrops[this.overed.ind].removeClass(this.options.cssClass + '-dropDrop');
			}

			if (overed){
				this.overed = overed;
				this.fireEvent('enter', [$objDrag, overed]);
				this.htmlDrops[overed.ind].addClass (this.options.cssClass + '-overDrop');
				this.htmlDrags[$objDrag.ind].addClass(this.options.cssClass + '-overDrag');
			} else {
				this.overed = null;
			}
		};
	},

	attach: function($objDrag){
		$objDrag.handles.addEvent('mousedown', $objDrag.bound.start);
		return $objDrag;
	},

	detach: function($objDrag){
		$objDrag.handles.removeEvent('mousedown', $objDrag.bound.start);
		return $objDrag;
	},
	
	
    // esta funcion es llamada cuando se ha completadp el proceso de drag.
	dragComplete: function($objDrag) {
		
//		console.log (this, $objDrag);
		
		//if (this.options.cssClass) this.htmlDrags[$objDrag.ind].removeClass(this.options.cssClass + '-dragDrag');
/*
//		drag.fireEvent('dragComplete', drag);
		this.fireEvent('dragComplete', this.elDrags[$indDrag]);

		// Si el drag estaba en un drop ... y es modo sendToBack = back ... hacemos efecto de opacidad.
		if (!this.dragInDrop && this.options.sendToBack == 'back') {
			//			drag.fxDrag.start ({opacity: this.options.opacity.drag});
			drag.setOpacity(this.options.opacity.drag);
		};

		// autoLevel ?. Si es asi, los nivelamos.
		if (this.options.autoLevel) this.levelDrags(this.elDrags[$indDrag]);

		// disparamos evento dragOverDrag ?
		/*
		if (this.arrOvered.length > 1) {
			var arrTmp = [];
			this.arrOvered.each(function(drg, iDrg){
				arrTmp[this.ordDrag[drg.ind]] = drg;
			}.bind(this));

			arrTmp.clean();
			arrTmp.reverse();

			// Disparamos evento onDragOverDrag
			this.fireEvent('dragOverDrag', [drag, arrTmp]);
		}
		// Disparamos evento onEmptyDragOverDrag
		else 
			this.fireEvent('emptyOverDrag', this.elDrags[$indDrag]);
			

		// Ya no tenemos drag Activo (candidato a ser obsoleto)
		this.dragAct = false;
		*/
	},

	
	/*
	Function: dropComplete
	Definimos el comportamiento cuando se realiza un drop comun y corriente.
	*/
	dropComplete: function(drag, $droppable){
/*
		this.fireEvent('drop', [drag, $droppable]);

        // Asignamos al drag.indDrop el indice al cual fue droppeado

		// Damos 100% de opacidad al drop
		//		console.log ((drags.overed).getStyle ('color'));
		// Definimos indice de Drag sobre Drop
		drag.indDrop = $droppable.ind;

		// Condiciones de vuelta sendToBack. Definimos coordenadas del elemento drag.
		switch (this.options.sendToBack) {
			case 'ini':
				drag.Pos.x = this.options.adjDrags[drag.ind].x;
				drag.Pos.y = this.options.adjDrags[drag.ind].y;
			break;

			case 'back':
				//drag.Pos.y = this.options.adjDrops[drags.overed.ind].y;
			break;

			case 'toDrop':
				drag.Pos.x = this.options.adjDrops[drag.indDrop].x;
				drag.Pos.y = this.options.adjDrops[drag.indDrop].y;
			break;
		};

		// Checqueo De MultiDrops ...
		if ((($droppable).qttDrops <= ($droppable).numDrops) || this.options.multiDrop == 'infinity') {

			// Chequemos condicion de trueDrop mediante el valor que retorna la funcion this.fndTrueDrop
			if (this.fncTrueDrop(drag, ($droppable))) {

				if (this.options.cssClass) {
					this.cssDrops[($droppable).ind].addClass(this.cssClass.trueDrop);
					//this.cssDrops[($droppable).ind].removeClass(this.cssClass.overDrop);
				};

				// Opacidad ?
				// ($droppable).fxDrop.start ({'opacity': this.options.opacity.trueDrop});
				($droppable).setOpacity(this.options.opacity.trueDrop);

				// No hay multiDrop ?
				if (this.options.multiDrop == 'infinity') {
					// Eliminamos drag en evento drop ?
					if (this.options.dragDelete) this.deleteDrag(drag);
					else this.sendToBack(drag);
				}

				else 
					if (($droppable).qttDrops == ($droppable).numDrops) {

						// Disparamos evento onmultiDrop en objeto DxD.Base
						this.fireEvent('multiDrop', [drag, $droppable]);

						// Eliminamos drag en evento multiDrop ?
						if (this.options.dragDelete) this.deleteDrag(drag);
						else this.sendToBack(drag);
        			};

				// Incrementamos la cantidad de drops
				($droppable).qttDrops++;

				// Asignamos el indice de drag que esta sobre el drop en drop.indDrag
				($droppable).indDrag = drag.ind;

				// Disparamos evento onTrueDrop en objeto DxD.Base
				this.fireEvent('trueDrop', [drag, $droppable]);

                // ... y tambien lo disparamos en cada drop.
				($droppable).fireEvent('trueDrop', [drag, $droppable]);

				// Disparamos onDropsComplete ?
				for (var i = 0, elementos = this.drops.length, str = ""; i < elementos; str += ((this.drops[i].indDrag) + 1) ? "" : "*", i++) {};
				str != "";
				if (!str) this.fireEvent('dropsComplete');

				//if (this.options.enableSnds[5]) soundManager.play('sndGood');
            }

			else {
				// SendToBack ?
				if (this.options.sendToBack) this.sendToBack(drag, this.drops);

				// if (this.options.cssClass) ($droppable).removeClass(this.cssClass.trueDrop);
				// if (this.options.cssClass) ($droppable).removeClass(this.cssClass.overDrop);
				if (this.options.cssClass) {
					//this.cssDrops[($droppable).ind].removeClass(this.cssClass.trueDrop);
					//this.cssDrops[($droppable).ind].removeClass(this.cssClass.overDrop);
				};

				// Disparamos evento onEmptyTrueDrop en objeto DxD.Base
				this.fireEvent('emptyTrueDrop', [drag, $droppable]);
				//if (this.options.enableSnds[4]) soundManager.play('sndError');

				// ... y tambien lo disparamos en cada drop.
				($droppable).fireEvent('emptyTrueDrop', [drag, $droppable]);

				// Opacidad ?
				//				($droppable).fxDrop.start ({'opacity': this.options.opacity.drop});
				($droppable).setOpacity(this.options.opacity.drop);

				if (this.options.textInDrop) ($droppable).set('text', ($droppable).text);
			};
		}
		else {
            // SendToBack ?
			if (this.options.sendToBack) this.sendToBack(drag, this.drops);
			//if (this.options.cssClass) ($droppable).removeClass(this.cssClass.overDrop);
			//if (this.options.cssClass) this.cssDrops[($droppable).ind].removeClass(this.cssClass.overDrop);
		};
    },
	
	fncTrueDrop: function(drag, drop){
		return true;
	},


    reset: function(){
		// Conf Ini Drags
		this.elDrags.each(function(drag, iD){
			//drag.removeClass(this.options.cssClass + '-enterDrag');
			//drag.removeClass(this.options.cssClass + '-dragDrag');
			drag.setDisplay(true);
			drag.setOpacity(this.options.opacity.drag);
			drag.indDrop = -1;
		}.bind(this));

		// Conf Ini Drags
		this.drops.each(function(drop, iD){
			//drop.removeClass(this.options.cssClass + '-overDrop');
			//drop.removeClass(this.options.cssClass + '-trueDrop');
			drop.setOpacity(this.options.opacity.drop);

			drop.qttDrops = 1;
		}.bind(this));
		*/
    },
    
    /*
     method: addDrag
     Agregamos un Drag mas al conjunto de Drags. (_Experimental_)
     
     parametros:
     drag - Elemento que agregamos al conjunto de Drags And Drops
     ver Tambien:
     <setIniBase>
     */
	addDrag: function(drag) {
		//this.setIniBase([drag]);
	},

	raseUpDrag: function($objDrag){
		this.htmlDrags[$objDrag.ind].setStyle('z-index', this.options.zIndexTop);
		$objDrag.zIndex = this.options.zIndexTop;
    },

	/*
	 method - levelDrags
     Eleva los Drags al hace un _mousedown_ y los reacomoda sobre el mismo. Luego los _baja_
     La finalidad de este metodo es poder _elevar_ y _bajar_ los drags en al clickaer sobre		 los mismos. Trabaja basicamente con el z-index del elemento drag. Este evento se ejecuta siempre y cuando <autoLevel> este seteado a true.
     
     parametros:
     drag - elemento drag.
     ver Tambien:
     var <autoLevel>, Method <setIniBase>
     */
    levelDrags: function($objDrag){
		// Nivelamos Drags mediante z-index. Acomodamos this.ordDrag
		var levelDrag = this.ordDrag[$objDrag.ind];

		for (var nvlDrag = levelDrag + 1; nvlDrag <= (this.elDrags.length - 1); nvlDrag++) {
			iDrag = this.ordDrag.indexOf(nvlDrag);
			this.ordDrag[iDrag] = nvlDrag - 1;
			this.htmlDrags[iDrag].setStyle('z-index', this.options.zIndexBottom + nvlDrag - 1);

			this.elDrags[iDrag].zIndex = nvlDrag + this.options.zIndexBottom - 1;
		};

		this.ordDrag[$objDrag.ind] = this.elDrags.length - 1;
		this.htmlDrags[$objDrag.ind].setStyle('z-index', this.options.zIndexBottom + this.elDrags.length - 1);
	},

	/*
	 method - adjustDrags
	 Ajustgamos los drags en funcion del array de configuracion <adjDrags>. Si o tenemos un array de ajuste los elementos drags se posicionaran en funcion de los propiedades de checkbox: width, height, margin, padding y top, left, width del nodo padre. 
     
	 parametros:
	 coords - Array de coordenadas x / y de cada elemento. Se utiliza la notacion JSon, similar a la entregada por <Element.getPosition>, <setPosition>, etc
	 
	 ver Tambien:
	 <adjDrags>
     */
	adjustDrags: function() {
		if (this.options.adjDrags != '') {
			// Recorremos todos los elementos Drags
			(this.htmlDrags).each(function(drag, iDrag){
			
				// Asignamos posicion inicial de los drags como propiedad de los mismos. drag.Pos
				//drag.Pos.x = this.options.adjDrags[iDrag].x;
				//drag.Pos.y = this.options.adjDrags[iDrag].y;
				
				// Ahora definimos su posición
				drag.setPosition(this.options.adjDrags[iDrag]);
			}, this);
		};
    },

	/*
	 method - adjustDrops
	 Ajustgamos los drops en funcion del array de configuracion <adjDrops>.
	
	 parametros:
	 coords - Array de coordenadas x / y de cada elemento. Se utiliza la notacion JSon, similar a la entregada por <Element.getPosition>, <setPosition>, etc
	 ver Tambien:
	 <adjDrops>
	 */
	adjustDrops: function(){
		// Recorremos todos los elementos Drags
		(this.elDrops).each(function($objDrop, $iD){
			$htmlNodeDrop = this.htmlDrops[$iD];
			// Asignamos Posicion Absoluta
			// drop.setStyle ('position', 'absolute');

			// Asignamos posicion inicial de los drags como propiedad de los mismos. drag.Pos
			//$htmlNodeDrop.Pos.x = (this.options.adjDrops[iDrop].x);
			//$htmlNodeDrop.Pos.y = (this.options.adjDrops[iDrop].y);

			// Ahora definimos su posición
			$htmlNodeDrop.setPosition(this.options.adjDrops[$iD]);
			$objDrop.pos = this.getPos ($htmlNodeDrop);
			
		}, this);
	},

	deleteDrag: function(drag){
		if (this.options.dragDelete) {
			if ($type(this.options.dragDelete) == 'boolean') drag.destroy();
			else drag.setDisplay(false);
		}
	},

	sendToBack: function($indDrag){
		// Enviamos al Drag a donde sea.
		this.elDrags[$indDrag].fx.start({
		//drag.setStyles({
			'left': this.elDrags[$indDrag].pos.x,
			'top': this.elDrags[$indDrag].pos.y,
			'opacity': this.options.opacity.drag
		});
	},
	
	getPos: function ($htmlNode) {
		return {
			left: $htmlNode.getStyle('left').toInt(),
			top: $htmlNode.getStyle('top').toInt(),
			right: $htmlNode.getStyle('left').toInt() + $htmlNode.getStyle('width').toInt(),
			bottom: $htmlNode.getStyle('top').toInt() + $htmlNode.getStyle('height').toInt()
		}
	}
});